/**
 * 
 */
package pb.plugin.openfolder.handler;

import java.awt.Desktop;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.Deque;
import java.util.LinkedList;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;

import static pb.plugin.openfolder.handler.Constants.COMMAND_ID;
import static pb.plugin.openfolder.handler.Constants.COMMAND_VALUE_COPY_CLIPBOARD;
import static pb.plugin.openfolder.handler.Constants.COMMAND_VALUE_OPEN_FOLDER;
import static pb.plugin.openfolder.handler.Constants.COMMAND_VALUE_ZIP_FOLDER;
import static pb.plugin.openfolder.handler.Constants.DIALOG_CONTENT_OPERATION_FAIL;
import static pb.plugin.openfolder.handler.Constants.DIALOG_CONTENT_P1;
import static pb.plugin.openfolder.handler.Constants.DIALOG_CONTENT_P2;
import static pb.plugin.openfolder.handler.Constants.DIALOG_CONTENT_ZIP_FAIL;
import static pb.plugin.openfolder.handler.Constants.DIALOG_CONTENT_ZIP_OK;
import static pb.plugin.openfolder.handler.Constants.DIALOG_TITLE;
import static pb.plugin.openfolder.handler.Constants.SLASH;
import static pb.plugin.openfolder.handler.Constants.ZIP_EXTENSION;

/**
 * @author Phillip VU <vuhongphat@hotmail.com>
 * 
 */
public class FolderHandler extends AbstractHandler
{
    @Override
    public Object execute( ExecutionEvent event )
        throws ExecutionException
    {
        // Shell shell = HandlerUtil.getActiveShell( event );
        // ISelection sel = HandlerUtil.getActiveMenuSelection( event );
        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();

        if( window != null )
        {
            IStructuredSelection selection = (IStructuredSelection) window.getSelectionService().getSelection();
            Object firstElement = selection.getFirstElement();

            if( firstElement instanceof IAdaptable )
            {
                IResource selectedResource = (IResource) ( (IAdaptable) firstElement ).getAdapter( IResource.class );
                String location, folderNameOfParent;
                IPath path;

                if( selectedResource == null )
                {
                    MessageDialog.openInformation( null, DIALOG_TITLE, DIALOG_CONTENT_OPERATION_FAIL );
                    return null;
                }

                // If selected resource is file type,, get the location of the folder that
                // contains that file ( get the location of parent folder )
                if( firstElement instanceof ICompilationUnit || firstElement instanceof IFile )
                {
                    path = selectedResource.getParent().getLocation();
                }
                else
                {
                    path = selectedResource.getLocation();
                }

                folderNameOfParent = path.lastSegment();
                location = path.toString();

                // determine with sub-menu is selected.
                String param = event.getParameter( COMMAND_ID );
                if( param.equals( COMMAND_VALUE_OPEN_FOLDER ) )
                {
                    openFolder( location );
                }
                else if( param.equals( COMMAND_VALUE_COPY_CLIPBOARD ) )
                {
                    copyLocationToClipboard( location );
                }
                else if( param.equals( COMMAND_VALUE_ZIP_FOLDER ) )
                {
                    // get the location of result file.
                    String zipFile = location.concat( SLASH ).concat( folderNameOfParent ).concat( ZIP_EXTENSION );
                    zipResources( new File( location ), new File( zipFile ) );
                    openFolder( location );
                }
            }
        }

        return null;
    }

    /**
     * Open a directory
     * 
     * @param location: path of the directory to be opened.
     */
    private void openFolder( String location )
    {
        Desktop desktop = null;
        // Before more Desktop API is used, first check
        // whether the API is supported by this particular
        // virtual machine (VM) on this particular host.
        if( Desktop.isDesktopSupported() )
        {
            desktop = Desktop.getDesktop();
        }
        File file = new File( location );
        try
        {
            desktop.open( file );
        }
        catch( IOException e )
        {
            e.printStackTrace();
            // TODO exception handling.
        }
    }

    /**
     * Copy the location of the selected resource (e.g: File, Class..) to clipboard
     * 
     * @param location : location of the selected resource.
     */
    private void copyLocationToClipboard( String location )
    {
        StringSelection stringSelection = new StringSelection( location );
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        clipboard.setContents( stringSelection, null );

        MessageDialog.openInformation( null, DIALOG_TITLE, DIALOG_CONTENT_P1 + location + DIALOG_CONTENT_P2 );
    }

    /**
     * 
     * @param directory
     * @param zipfile
     * @throws IOException
     */
    public static void zipResources( File directory, File zipfile )
    {
        URI base = directory.toURI();
        Deque<File> queue = new LinkedList<File>();
        File[] listFiles = directory.listFiles();
        queue.push( directory );
        OutputStream out = null;
        boolean isOK = true;

        ZipOutputStream zout = null;
        try
        {
            int temp = 0;
            out = new FileOutputStream( zipfile );
            zout = new ZipOutputStream( out );
            while( !queue.isEmpty() )
            {
                if( temp != 0 )
                {
                    directory = queue.pop();
                    listFiles = directory.listFiles();
                }
                else
                {
                    queue.pop();
                    temp++;
                }

                for( File kid : listFiles )
                {
                    String name = base.relativize( kid.toURI() ).getPath();
                    if( kid.isDirectory() )
                    {
                        queue.push( kid );
                        name = name.endsWith( SLASH ) ? name : name + SLASH;
                        zout.putNextEntry( new ZipEntry( name ) );
                    }
                    else
                    {
                        zout.putNextEntry( new ZipEntry( name ) );
                        copyFile( kid, zout );
                        zout.closeEntry();
                    }
                }
            }
        }
        catch( FileNotFoundException exp )
        {
            isOK = false;
            // TODO Auto-generated catch block
            exp.printStackTrace();
        }
        catch( IOException exp )
        {
            isOK = false;
            // TODO Auto-generated catch block
            exp.printStackTrace();
        }
        finally
        {
            try
            {
                if( zout != null )
                {
                    zout.close();
                }
                if( out != null )
                {
                    out.close();
                }
            }
            catch( IOException exp )
            {
                isOK = false;
                // TODO Auto-generated catch block
                exp.printStackTrace();
            }

            if( isOK )
            {
                MessageDialog.openInformation( null, DIALOG_TITLE, DIALOG_CONTENT_ZIP_OK );
            }
            else
            {
                MessageDialog.openInformation( null, DIALOG_TITLE, DIALOG_CONTENT_ZIP_FAIL );
            }
        }
    }

    /**
     * 
     * @param file
     * @param out
     * @throws IOException
     */
    private static void copyFile( File file, OutputStream out )
        throws IOException
    {
        InputStream in = new FileInputStream( file );
        try
        {
            byte[] buffer = new byte[ 2048 ];
            while( true )
            {
                int readCount = in.read( buffer );
                if( readCount < 0 )
                {
                    break;
                }
                out.write( buffer, 0, readCount );
            }
        }
        finally
        {
            in.close();
        }
    }
}